#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyData.h>
#include <vtkPoints.h>
#include <vtkCellArray.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkVolume16Reader.h>

#include <vtkStructuredPoints.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>

#include <vtkTexture.h>
#include <vtkPNMReader.h>

#include <math.h>

#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

#include "Cube.h"
#include "Point.h"
#include "CasosBase.h"

int main( int argc, char *argv[] )
{
  int i, ijk[3], dims[3];

  float spx = 1.0;
  float spy = 1.0;
  float spz = 0.46875;

  if (argc!=2)
  {
  cerr << "Argumentos incorrectos: ./binario umbral";
  exit(-1);
  }

  double umbral = atof(argv[1]);

	//Vector de los 15 casos base.
	std::vector<Index> casosBase;
	inicializarCasosBase(casosBase);

  // Las siguientes instrucciones permiten leer los datos del volumen
  // de disco. No es necesario tocar nada en estas instrucciones.

  vtkVolume16Reader *v16 = vtkVolume16Reader::New();
  v16->SetDataDimensions (64,64);
  v16->SetImageRange (1,93);
  v16->SetDataByteOrderToLittleEndian();
  v16->SetFilePrefix ("./vtkdata/headsq/quarter");
  v16->SetDataSpacing(spx,spy,spz);
  v16->Update();

  // Mediante las siguientes instrucciones creamos una estructura que
  // nos permite acceder a los valores de los puntos del volumen anterior.

  vtkStructuredPoints *vol_v16 = (vtkStructuredPoints *)v16->GetOutput();
  vol_v16->GetDimensions(dims);

  // Estas son las dimensiones del volumen leido

  fprintf(stderr,"Cabeza, dimensiones: %d %d %d\n",dims[0],dims[1],dims[2]);

  // A continuacion recorremos todos los puntos del volumen y accedemos a su
  // valor. Aqui hay que introducir el codigo del algoritmo de marching cubes.

  vtkFloatArray *v16_datos=(vtkFloatArray *) vol_v16->GetPointData()->GetScalars();


  Cube currentCube;
  Model3D calculatedModel3D;

  for(ijk[0] = 0; ijk[0] < dims[0]-1 ; ijk[0]++)
    for(ijk[1] = 0; ijk[1] < dims[1]-1 ; ijk[1]++)
      for(ijk[2] = 0; ijk[2] < dims[2]-1 ; ijk[2]++)
        {
    	  // Buscamos el cubo correspondiente
    	  currentCube.generateCube(ijk, vol_v16, v16_datos);
    	  currentCube.calculateIndex(umbral);

    	  //calcular que caso base es
    	  Index tmpIndex;
    	  //hacemos cada rotacion posbile del indice
    	  for(int rotx=0; rotx<4; rotx++)
    		  for(int roty=0; roty<4; roty++)
    			  for(int rotz=0; rotz<4; rotz++)
    			  {
    				  tmpIndex =  (currentCube.getIndex().rotateIndex(rotx, roty, rotz));
    				  //comprobamos de que caso base se trata
    				  for(int baseCaseIterator=0; baseCaseIterator < 15 ; baseCaseIterator++)
    				  {
							 if(tmpIndex.esCasoBase(baseCaseIterator, casosBase) || tmpIndex.complementario().esCasoBase(baseCaseIterator, casosBase))
							 {
								 //Caso base encontrado. Obtener Model3D:
								  Cube rotatedCube=currentCube.rotate(rotx, roty, rotz);
								  calculatedModel3D.addTriangles(baseCaseIterator, rotatedCube, rotx, roty, rotz,umbral);
								  rotx= 4; roty= 4; rotz= 4;
								  break;
							 }
    				  }

    			  }
        }


  vtkPoints *vertex_points = vtkPoints::New();
  vtkCellArray *polys = vtkCellArray::New();

  int numTriangles=calculatedModel3D.getTriangles().size();

  //Añadimoslos puntos a VTK
  float** puntos;
  puntos = (float**) malloc(numTriangles*3*sizeof(float*));

  for (int t = 0; t < numTriangles*3; t++)
	  puntos[t] = (float*) malloc(3*sizeof(float));


  for(unsigned int numTriangle=0;numTriangle < numTriangles; numTriangle++){
	  Triangle tmpTriangle=  calculatedModel3D.getTriangle(numTriangle);
	  //punto 1
	  puntos[numTriangle*3][0]=tmpTriangle.p1.x;
	  puntos[numTriangle*3][1]=tmpTriangle.p1.y;
	  puntos[numTriangle*3][2]=tmpTriangle.p1.z;
	  //punto 2
	  puntos[numTriangle*3+1][0]=tmpTriangle.p2.x;
	  puntos[numTriangle*3+1][1]=tmpTriangle.p2.y;
	  puntos[numTriangle*3+1][2]=tmpTriangle.p2.z;
	  //punto3
	  puntos[numTriangle*3+2][0]=tmpTriangle.p3.x;
	  puntos[numTriangle*3+2][1]=tmpTriangle.p3.y;
	  puntos[numTriangle*3+2][2]=tmpTriangle.p3.z;

  }
  for(i = 0; i < numTriangles*3; i++)
     vertex_points->InsertPoint(i,puntos[i]);

  //Añadimos las celdas al VTK
  int **cells;
  cells = (int**) malloc(numTriangles*sizeof(int*));
    for (unsigned int g = 0; g < numTriangles; g++)
    	cells[g] = (int*) malloc(3*sizeof(int));

    for(unsigned int cellIterator=0; cellIterator < numTriangles; cellIterator++){
   	  cells[cellIterator][0]=cellIterator*3;
   	 	  cells[cellIterator][1]=cellIterator*3+1;
   	 	  cells[cellIterator][2]=cellIterator*3+2;
     }

  for(i = 0; i < numTriangles; i++)
    polys->InsertNextCell(3,cells[i]);



  // A partir de aqui en principio no hay que tocar nada.

  vtkPolyData *iso_surface = vtkPolyData::New();
  iso_surface->SetPoints(vertex_points);
  iso_surface->SetPolys(polys);

  vertex_points->Delete();
  polys->Delete();

  iso_surface->BuildCells();
  iso_surface->BuildLinks();

  vtkPolyDataMapper *mapper = vtkPolyDataMapper::New();
  mapper->SetInput(iso_surface);

  vtkActor *actor = vtkActor::New();
  actor->SetMapper(mapper);

  vtkCamera *camera = vtkCamera::New();
  camera->SetPosition(1,1,1);
  camera->SetFocalPoint(0,0,0);

  vtkRenderer *renderer = vtkRenderer::New();
  vtkRenderWindow *renWin = vtkRenderWindow::New();
  renWin->AddRenderer(renderer);

  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
  iren->SetRenderWindow(renWin);

  renderer->AddActor(actor);
  renderer->SetActiveCamera(camera);
  renderer->ResetCamera();
  renderer->SetBackground(0,0,0);

  renWin->SetSize(500,500);

  // interact with data
  renWin->Render();
  iren->Start();

  // Clean up

  iso_surface->Delete();
  mapper->Delete();
  actor->Delete();
  camera->Delete();
  renderer->Delete();
  renWin->Delete();
  iren->Delete();
}




